<?php
/**
*
* @package shopBB
* @copyright (c) 2008 EXreaction, Lithium Studios
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/

if (!defined('IN_PHPBB'))
{
	exit;
}

class shop
{
	// Holds an array of the item data $item_data[$item_id] = array(sql_row)
	public static $item_data = array();

	// Holds an array of the category data $category_data[$category_id] = array(sql_row)
	public static $category_data = array();

	// The currency that will be used
	public static $currency = '';

	/**
	* Constructor
	*/
	public function __construct()
	{
		global $cache, $config, $db;

		// Always get the categories
		/*self::$category_data = $cache->get('_shop_categories');

		if (self::$category_data === false)
		{
			self::$category_data = array();
			$result = $db->sql_query('SELECT * FROM ' . SHOP_CATEGORIES_TABLE . ' ORDER BY left_id ASC');
			while ($row = $db->sql_fetchrow($result))
			{
				self::$category_data[$row['category_id']] = $row;
			}
			$db->sql_freeresult($result);
		}

		$cache->put('_shop_categories', self::$category_data);*/

		// set the currency
		self::$currency = $config['shop_currency'];
	}

	/**
	* Add a shop language file
	*
	* Includes fallback modes to the default lang and en.
	*/
	public static function add_lang($file, $shop = true)
	{
		if (is_array($file))
		{
			foreach ($file as $params)
			{
				call_user_func_array(array('self', 'add_lang'), $params);
			}
			return;
		}

		global $config, $user;

		$file_path = ($shop) ? 'mods/shop/' : '';

		if (file_exists(PHPBB_ROOT_PATH . $user->lang_path . $user->lang_name . 'mods/shop/' . $file))
		{
			$user->add_lang($file_path . $file);
		}
		else if (file_exists(PHPBB_ROOT_PATH . $user->lang_path . basename($config['default_lang']) . 'mods/shop/' . $file))
		{
			$user->add_lang('./../' . basename($config['default_lang']) . $file_path . $file);
		}
		else
		{
			$user->add_lang('./../en/' . $file_path . $file);
		}
	}

	/**
	* Set template function
	*/
	public static function set_template($file, $full_path = false)
	{
		global $template;

		$template->set_filenames(array(
			'body' => (($full_path) ? $file : "shop/{$file}.html"),
		));
	}

	/**
	* Page Header function
	*/
	public static function header($lang)
	{
		global $user;

		page_header(((isset($user->lang[$lang])) ? $user->lang[$lang] : $lang), false);
	}

	/**
	* Format an error
	*/
	public static function format_error($error_ary)
	{
		global $user;

		foreach ($error_ary as &$error)
		{
			$error = (isset($user->lang[$error])) ? $user->lang[$error] : $error;
		}

		return implode('<br />', $error_ary);
	}

/**
* Item Functions ----------------------------------------------------------------------------------
*/

	/**
	* Get the items based on the inputted selection data
	*
	* @param array $selection_data Options:
	*	category_id, item_ids, user_id,
	*	order_by, order_dir, start, limit
	*	mode, parse_bbcode (assumed true if not sent),
	*/
	public static function get_items($selection_data)
	{
		global $db;

		$sql_array = array(
			'SELECT'	=> '*',
			'FROM'		=> array(
				SHOP_ITEMS_TABLE	=> array('si'),
				USERS_TABLE			=> array('u'),
			),
			'WHERE'		=> array(
				'u.user_id = si.user_id',
			),
			'ORDER_BY'	=> ((isset($selection_data['order_by'])) ? $selection_data['order_by'] : 'si.item_id') . ' ' . ((isset($selection_data['order_dir'])) ? $selection_data['order_dir'] : 'DESC'),
		);

		// Categorys
		if (isset($selection_data['category_id']) && (is_array($selection_data['category_id']) || $selection_data['category_id'] != 0))
		{
			$sql_array['LEFT_JOIN'][] = array(
				'FROM'		=> array(SHOP_ITEMS_IN_CATS_TABLE => 'sic'),
				'ON'		=> 'sic.item_id = si.item_id',
			);
			$sql_array['WHERE'][] = (is_array($selection_data['category_id'])) ? $db->sql_in_set('sic.category_id', $selection_data['category_id']) : 'sic.category_id = ' . (int) $selection_data['category_id'];
		}

		if (isset($selection_data['item']) && $selection_data['item'])
		{
			$sql_array['WHERE'][] = '(si.item_id = ' . (int) $selection_data['item'] . ' OR si.item_name_clean = \'' . $db->sql_escape($selection_data['item']) . '\')';
		}
		if (isset($selection_data['item_ids']) && $selection_data['item_ids'])
		{
			$sql_array['WHERE'][] = (is_array($selection_data['item_ids'])) ? $db->sql_in_set('si.item_id', $selection_data['item_ids']) : 'si.item_id = ' . (int) $selection_data['item_ids'];
		}
		if (isset($selection_data['user_id']) && $selection_data['user_id'])
		{
			$sql_array['WHERE'][] = (is_array($selection_data['user_id'])) ? $db->sql_in_set('si.user_id', $selection_data['user_id']) : 'si.user_id = ' . (int) $selection_data['user_id'];
		}

		// Specialized modes
		if (isset($selection_data['mode']))
		{
			switch ($selection_data['mode'])
			{
				// Return a count of the number of items based on the selection data
				case 'count' :
					$sql_array['SELECT'] = 'count(item_id) AS count';
					$sql_array['WHERE'] = implode(' AND ', $sql_array['WHERE']);
					unset($sql_ary['ORDER_BY']);
					$sql = $db->sql_build_query('SELECT', $sql_array);
					$db->sql_query($sql);

					return $db->sql_fetchfield('count');
				break;
			}
		}

		$sql_array['WHERE'] = implode(' AND ', $sql_array['WHERE']);
		$sql = $db->sql_build_query('SELECT', $sql_array);

		if (isset($selection_data['limit']))
		{
			$result = $db->sql_query_limit($sql, $selection_data['limit'], ((isset($selection_data['start'])) ? $selection_data['start'] : 0));
		}
		else
		{
			$result = $db->sql_query($sql);
		}

		$item_ids = array();
		while ($row = $db->sql_fetchrow($result))
		{
			// Parse the message and store it in item_parsed_desc
			$row['item_parsed_desc'] = $row['item_desc'];
			$row['item_parsed_desc'] = generate_text_for_display($row['item_parsed_desc'], $row['item_desc_uid'], $row['item_desc_bitfield'], $row['item_desc_options']);

			self::$item_data[$row['item_id']] = $row;

			$item_ids[] = $row['item_id'];
		}
		$db->sql_freeresult($result);

		return $item_ids;
	}

	/**
	* Prepare raw info from the database from the item for output
	*
	* @return Returns an array ready to be outputted to the template.
	*/
	public static function prepare_item($item_id)
	{
		global $user;

		$row = self::$item_data[$item_id];

		$return = array(
			'ITEM_ID'				=> $item_id,
			'ITEM_NAME'				=> $row['item_name'],
			'ITEM_PARSED_DESC'		=> $row['item_parsed_desc'],

			'ITEM_TIME'				=> $user->format_date($row['item_time']),

			'USER_FULL'				=> get_username_string('full', $row['user_id'], $row['username'], $row['user_colour']),
			'USER_AVATAR'			=> '',
			'USER_RANK_TITLE'		=> '',
			'USER_RANK_IMG'			=> '',
			'USER_POSTS'			=> $row['user_posts'],
			'USER_JOINED'			=> '',
			'USER_FROM'				=> '',
			'U_USER'				=> get_username_string('profile', $row['user_id'], $row['username'], $row['user_colour']),
			'U_USER_PM'				=> '',
			'U_USER_EMAIL'			=> '',
			'U_USER_WWW'			=> '',
			'U_USER_MSN'			=> '',
			'U_USER_ICQ'			=> '',
			'U_USER_YIM'			=> '',
			'U_USER_AIM'			=> '',
			'U_USER_JABBER'			=> '',

			'U_APPROVE'		=> self::build_url($item_id, 'approve'),
			'U_DELETE'		=> self::build_url($item_id, 'delete'),
			'U_EDIT'		=> self::build_url($item_id, 'edit'),
			'U_PURCHASE'	=> self::build_url($item_id, 'purchase'),
			'U_REPORT'		=> self::build_url($item_id, 'report'),
			'U_VIEW'		=> self::build_url($item_id),
		);

		shop_plugins::plugin_do_ref('shop_prepare_item', $return);

		return $return;
	}

	/**
	* Add/edit/delete an item
	*
	* @param array $item_data An array filled with the data that is required to be submitted to add/edit the item.
	* @param string $mode Available modes are as follows: 'add', 'edit', 'delete'
	* @param array $error Any errors will be listed in this array.
	* @param bool $parse_bbcode True to have BBCode automatically parsed for the item description.
	*/
	public static function submit_item($mode = 'add', &$item_data, &$error, $parse_bbcode = true)
	{
		global $db, $user;

		if (isset($item_data['item_id']))
		{
			$item_data['item_id'] = (int) $item_data['item_id'];
		}

		if ($mode == 'edit' || $mode == 'delete')
		{
			if (!isset(self::$item_data[$item_data['item_id']]))
			{
				self::get_items(array('item_ids' => $item_data['item_id']));

				if (!isset(self::$item_data[$item_data['item_id']]))
				{
					trigger_error('ITEM_NOT_EXIST');
				}
			}

			if ($mode == 'delete')
			{
				// Do not change anything else other than the following when soft deleting.
				$item_data = array(
					'item_id'				=> $item_data['item_id'],
					'item_delete'			=> (isset($item_data['item_delete'])) ? $item_data['item_delete'] : $user->data['user_id'],
					'item_delete_time'		=> (isset($item_data['item_delete_time'])) ? $item_data['item_delete_time'] : time(),
					'item_delete_reason'	=> (isset($item_data['item_delete_reason'])) ? $item_data['item_delete_reason'] : '',
				);

				$mode = 'edit';
			}

			// Merge existing data when editing/deleting
			$item_data = array_merge(self::$item_data[$item_data['item_id']], $item_data);
			decode_message($item_data['item_desc'], $item_data['item_desc_uid']);
		}

		$sql_ary = array(
			'user_id'				=> (int)	(isset($item_data['user_id'])) ? $item_data['user_id'] : $user->data['user_id'],
			'user_ip'				=> (string)	(isset($item_data['user_ip'])) ? $item_data['user_ip'] : $user->ip,

			'item_name'				=> (string)	(isset($item_data['item_name'])) ? utf8_normalize_nfc($item_data['item_name']) : '',
			'item_name_clean'		=> (string)	(isset($item_data['item_name'])) ? utf8_clean_string($item_data['item_name']) : '',
			'item_desc'				=> (string)	(isset($item_data['item_desc'])) ? utf8_normalize_nfc($item_data['item_desc']) : '',
			'item_desc_uid'			=> (string)	(isset($item_data['item_desc_uid'])) ? $item_data['item_desc_uid'] : '',
			'item_desc_bitfield'	=> (string)	(isset($item_data['item_desc_bitfield'])) ? $item_data['item_desc_bitfield'] : '',
			'item_desc_options'		=> (int)	(isset($item_data['item_desc_options'])) ? $item_data['item_desc_options'] : 7,
			'item_time'				=> (int)	(isset($item_data['item_time'])) ? $item_data['item_time'] : time(),
			'item_approved'			=> (bool)	(isset($item_data['item_approved'])) ? $item_data['item_approved'] : true,
			'item_reported'			=> (bool)	(isset($item_data['item_reported'])) ? $item_data['item_reported'] : false,

			// Item cost
			'item_cost'				=> (float)	(isset($item_data['item_cost'])) ? $item_data['item_cost'] : 0,

			// Number of items available for sale.  -1 for unlimited.
			'item_inventory'		=> (int)	(isset($item_data['item_inventory'])) ? $item_data['item_inventory'] : -1,

			// The user_id of the one who last edited it, the edit count, the edit time, and the edit reason
			'item_edit'				=> (int)	(isset($item_data['item_edit'])) ? $item_data['item_edit'] : 0,
			'item_edit_cnt'			=> (int)	(isset($item_data['item_edit_cnt'])) ? $item_data['item_edit_cnt'] : 0,
			'item_edit_time'		=> (int)	(isset($item_data['item_edit_time'])) ? $item_data['item_edit_time'] : 0,
			'item_edit_reason'		=> (string)	(isset($item_data['item_edit_reason'])) ? utf8_normalize_nfc($item_data['item_edit_reason']) : '',

			// The user_id of the one who deleted it, the delete time, and the delete reason
			'item_delete'			=> (int)	(isset($item_data['item_delete'])) ? $item_data['item_delete'] : 0,
			'item_delete_time'		=> (int)	(isset($item_data['item_delete_time'])) ? $item_data['item_delete_time'] : 0,
			'item_delete_reason'	=> (string)	(isset($item_data['item_delete_reason'])) ? utf8_normalize_nfc($item_data['item_delete_reason']) : '',
		);

		// Check for errors -----------
		// Did they not send a name?
		if ($sql_ary['item_name'] == '')
		{
			$error[] = 'ITEM_NAME_EMPTY';
		}
		// If they are adding a new item or editing one and change the name we need to check if one by that name exists already.
		else if ($mode == 'add' || ($mode == 'edit' && $sql_ary['item_name'] != self::$item_data[$item_data['item_id']]['item_name']))
		{
			// Check if the item name has been used already (this mod only allows the item name to be used once).
			$result = $db->sql_query('SELECT item_id FROM ' . SHOP_ITEMS_TABLE . ' WHERE item_name_clean = \'' . $db->sql_escape($sql_ary['item_name_clean']) . '\'');
			$title_row = $db->sql_fetchrow($result);
			if ($title_row)
			{
				$error[] = sprintf($user->lang['ITEM_NAME_EXIST'], shop_item_url($title_row['item_id']));
			}
			$db->sql_freeresult($result);

			if (is_numeric($sql_ary['item_name']))
			{
				$error[] = 'ITEM_NAME_INVALID';
			}
		}

		extract(shop_plugins::plugin_do('shop_submit_item', compact('item_data', 'mode', 'parse_bbcode', 'sql_ary', 'error')));

		// If there were any errors return.
		if (sizeof($error))
		{
			return;
		}

		// Parse the BBCode
		if ($parse_bbcode)
		{
			if (!function_exists('generate_text_for_storage'))
			{
				include(PHPBB_ROOT_PATH . 'includes/functions_content.' . PHP_EXT);
			}

			$enable_bbcode = $enable_magic_url = $enable_smilies = false;
			reverse_storage_options($sql_ary['item_desc_options'], &$enable_bbcode, &$enable_magic_url, &$enable_smilies);
			generate_text_for_storage($sql_ary['item_desc'], $sql_ary['item_desc_uid'], $sql_ary['item_desc_bitfield'], $sql_ary['item_desc_options'], $enable_bbcode, $enable_magic_url, $enable_smilies);
		}

		switch ($mode)
		{
			case 'add' :
				$db->sql_query('INSERT INTO ' . SHOP_ITEMS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
				$item_data['item_id'] = $db->sql_nextid();
			break;

			case 'edit' :
				$db->sql_query('UPDATE ' . SHOP_ITEMS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' WHERE item_id = ' . $item_data['item_id']);
			break;
		}

		// Set/update the item data in the self::$item_data array (for use when building the url later)
		self::$item_data[$item_data['item_id']] = (isset(self::$item_data[$item_data['item_id']])) ? array_merge(self::$item_data[$item_data['item_id']], $sql_ary) : $sql_ary;

		// Set/update the categories this item is in
		//self::set_item_categorys($item_data['item_id'], (isset($item_data['category_ids'])) ? $item_data['category_ids'] : array());
	}

/**
* URL Functions -----------------------------------------------------------------------------------
*/

	/**
	* Build URL
	*
	* Currently does not support SEO urls.
	*/
	public static function build_url($url_data = array(), $url_extra = array(), $force_no_seo = false)
	{
		global $config;

		// Allows a shortcut for the item.  Send the item_id as $url_data and the mode (if any) through $url_extra.
		if (!is_array($url_data))
		{
			if (!is_array($url_extra))
			{
				$url_data = array('item' => $url_data, 'page' => 'item', 'mode' => $url_extra);
			}
			else
			{
				$url_data = array('item' => $url_data);
			}
		}

		$url = $anchor = '';

		if (isset($url_data['anchor']))
		{
			$anchor = '#' . $url_data['anchor'];
			unset($url_data['anchor']);
		}

		if ($config['shop_seo'] && ((sizeof($url_data) == 1) && isset($url_data['item'])))
		{
			if (isset(shop::$item_data[$url_data['item']]))
			{
				return generate_board_url() . '/shop/' . shop::$item_data[$url_data['item']]['item_name_clean'] . '.html';
			}
		}

		foreach ($url_data as $name => $var)
		{
			if ($var == '*skip*') continue;

			$url .= '&amp;' . $name . '=' . $var;
		}

		$url = substr($url, 5); // remove the first &amp;
		return append_sid(SHOPBB_PATH . 'index.' . PHP_EXT, $url) . $anchor;
	}

/**
* Category Functions ------------------------------------------------------------------------------
*/

	/**
	* Handle the categories
	*
	* @param int $parent_id If this is set to something other than 0 it will only list categories under the category_id given
	* @param string $block Set the name of the block to output it to.
	* @param bool $ignore_subcats True to ignore subcategories, false to display them.
	* @param array $category_list If you want to send the already gotten category list...
	*/
	public static function output_categories($parent_id = 0, $block = 'category_row', $ignore_subcats = false)
	{
		global $config, $template, $user;

		foreach (self::$category_data as $category_id => $row)
		{
			if ($parent_id == $row['category_id'] && !$ignore_subcats)
			{
				$template->assign_vars(array(
					'U_CURRENT_CATEGORY'	=> shop_url(array('c' => $row['category_id'])),
					'CURRENT_CATEGORY'		=> $row['category_name'],
					'CATEGORY_RULES'		=> generate_text_for_display($row['category_rules'], $row['category_rules_uid'], $row['category_rules_bitfield'], $row['category_rules_options']),
				));
			}

			if ($parent_id == $row['parent_id'])
			{
				$template->assign_block_vars($block, array(
					'CATEGORY_NAME'			=> $row['category_name'],
					'CATEGORY_DESCRIPTION'	=> generate_text_for_display($row['category_desc'], $row['category_desc_uid'], $row['category_desc_bitfield'], $row['category_desc_options']),
					'ITEMS'					=> $row['item_count'],
					'U_CATEGORY'			=> shop_url(array('c' => $row['category_id'])),
					'S_SUBCATEGORY'			=> ($row['right_id'] > ($row['left_id'] + 1) && !$ignore_subcats),
					'L_SUBCATEGORY'			=> ($row['right_id'] > ($row['left_id'] + 3)) ? $user->lang['SUBCATEGORIES'] : $user->lang['SUBCATEGORY'],
				));

				// If not, then there are subcategories
				if ($row['right_id'] > ($row['left_id'] + 1) && !$ignore_subcats)
				{
					self::output_categories($row['category_id'], 'category_row.subcategory', true);
				}
			}
		}
	}

	/**
	* Set the categories that an item is in
	*/
	public static function set_item_categorys($item_id, $category_ids)
	{
		global $cache, $db;

		$item_id = (int) $item_id;

		if (!is_array($category_ids))
		{
			$category_ids = array($category_ids);
		}
		$category_ids = array_unique($category_ids);

		// This is the easy way to do it.  It isn't the best way to do it, but it takes the least work, has the least potential for bugs, and works just fine IMO.
		$current_cats = array();
		$result = $db->sql_query('SELECT category_id FROM ' . SHOP_ITEMS_IN_CATEGORIES_TABLE . ' WHERE item_id = ' . $item_id);
		while ($row = $db->sql_fetchrow($result))
		{
			$current_cats[] = $row['category_id'];
			self::$category_data[$row['category_id']]['item_count']--;
		}
		$db->sql_freeresult($result);

		if (sizeof($current_cats))
		{
			$db->sql_query('UPDATE ' . SHOP_CATEGORIES_TABLE . ' SET item_count = item_count - 1 WHERE ' . $db->sql_in_set('category_id', $current_cats));
			$db->sql_query('DELETE FROM ' . SHOP_ITEMS_IN_CATEGORIES_TABLE . ' WHERE item_id = ' . $item_id);
		}

		if (sizeof($category_ids))
		{
			foreach ($category_ids as $id)
			{
				$db->sql_query('INSERT INTO ' . SHOP_ITEMS_IN_CATEGORIES_TABLE . ' ' . $db->sql_build_array('INSERT', array('item_id' => $item_id, 'category_id' => (int) $id)));
				self::$category_data[$id]['item_count']++;
			}
			$db->sql_query('UPDATE ' . SHOP_CATEGORIES_TABLE . ' SET item_count = item_count + 1 WHERE ' . $db->sql_in_set('category_id', $category_ids));
		}

		// Update the data in the cache
		$cache->put('_shop_categories', self::$category_data);
	}

	/**
	* Simple version of jumpbox, just lists categories
	* Copied from make_forum_select
	*/
	public static function make_category_select($select_id = false, $ignore_id = false, $return_array = false)
	{
		global $db;

		// This query is identical to the jumpbox one
		$sql = 'SELECT category_id, category_name, parent_id, left_id, right_id
			FROM ' . SHOP_CATEGORIES_TABLE . '
			ORDER BY left_id ASC';
		$result = $db->sql_query($sql);

		$right = 0;
		$padding_store = array('0' => '');
		$padding = '';
		$category_list = ($return_array) ? array() : '';

		// Sometimes it could happen that forums will be displayed here not be displayed within the index page
		// This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
		// If this happens, the padding could be "broken"

		while ($row = $db->sql_fetchrow($result))
		{
			if ($row['left_id'] < $right)
			{
				$padding .= '&nbsp; &nbsp;';
				$padding_store[$row['parent_id']] = $padding;
			}
			else if ($row['left_id'] > $right + 1)
			{
				$padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
			}

			$right = $row['right_id'];
			$disabled = false;

			if ((is_array($ignore_id) && in_array($row['category_id'], $ignore_id)) || $row['category_id'] == $ignore_id)
			{
				$disabled = true;
			}

			if ($return_array)
			{
				// Include some more information...
				$selected = (is_array($select_id)) ? ((in_array($row['category_id'], $select_id)) ? true : false) : (($row['category_id'] == $select_id) ? true : false);
				$category_list[$row['category_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
			}
			else
			{
				$selected = (is_array($select_id)) ? ((in_array($row['category_id'], $select_id)) ? ' selected="selected"' : '') : (($row['category_id'] == $select_id) ? ' selected="selected"' : '');
				$category_list .= '<option value="' . $row['category_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['category_name'] . '</option>';
			}
		}
		$db->sql_freeresult($result);
		unset($padding_store);

		return $category_list;
	}

	/**
	* Get category branch
	* From get_forum_branch
	*/
	public static function get_category_branch($category_id, $type = 'all', $order = 'descending', $include_forum = true)
	{
		global $db;

		switch ($type)
		{
			case 'parents':
				$condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
			break;

			case 'children':
				$condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
			break;

			default:
				$condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
			break;
		}

		$rows = array();

		$sql = 'SELECT f2.*
			FROM ' . SHOP_CATEGORIES_TABLE . ' f1
			LEFT JOIN ' . SHOP_CATEGORIES_TABLE . " f2 ON ($condition)
			WHERE f1.category_id = " . intval($category_id) . "
			ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
		$result = $db->sql_query($sql);

		while ($row = $db->sql_fetchrow($result))
		{
			if (!$include_forum && $row['category_id'] == $category_id)
			{
				continue;
			}

			$rows[] = $row;
		}
		$db->sql_freeresult($result);

		return $rows;
	}
}

?>